#pragma once
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

#include <string>
#include <vector>

#ifdef _WIN32

#include "xdma_public.h"
#include "pcie_win.h"

#include <strsafe.h>
#include <Windows.h>
#include <SetupAPI.h>
#include <INITGUID.H>
#include <WinIoCtl.h>
//#include <afxmt.h>

enum Direction {
  C2H, // client to host - read
  H2C  // host to client - write
};


#pragma comment(lib, "setupapi.lib")

typedef struct {
  BOOL verbose;
  char *device;
  char *file;
  BYTE *data;
  LARGE_INTEGER address;
  DWORD size;
  enum Direction direction;
  size_t alignment;
  BOOL binary;
} Options;


// PCIE API related.

class CPcieComm {
public:
  CPcieComm();
  ~CPcieComm();

public:
  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  int pcie_open();

  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  int pcie_write(UINT addr_offset, BYTE *data, UINT len, std::string device="h2c_0");

  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  int pcie_read(UINT addr_offset, BYTE *data, UINT &len, std::string device="c2h_0");

  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  bool pcie_is_open();

  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  int pcie_close();

private:
  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  BYTE *allocate_buffer(size_t size, size_t alignment);

  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  int get_devices(GUID guid, char *devpath, size_t len_devpath);

  /*
   * Function des:
   * In:
   * Out:
   * Return:
   */
  int cmd_parse(std::vector<std::string> args, BYTE *data = NULL, UINT len = 0);

  /*
   * Function des: adjust data sequence.
   * In:
   * Out:
   * Return:
   */
  int data_sequence_adjust(UINT addr, BYTE *data, UINT len, bool b_write);

private:
  BYTE data_buf[1024];

  // thread mutex
  CRITICAL_SECTION cs;

  // Open flag
  bool b_pcie_open;

  // Write operation
  Options options_write;

  // Read operation
  Options options_read;

  // device handle.
  HANDLE read_handle;
  HANDLE write_handle;

  HANDLE user_handle;

};

#endif


